package com.example.lt.service.video.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.lt.config.QiNiuConfig;
import com.example.lt.constant.AuditStatus;
import com.example.lt.constant.RedisConstant;
import com.example.lt.context.BaseContext;
import com.example.lt.context.LocalCache;
import com.example.lt.entity.File;
import com.example.lt.entity.queryPage.BasePage;
import com.example.lt.entity.user.User;
import com.example.lt.entity.user.UserModel;
import com.example.lt.entity.video.*;
import com.example.lt.entity.vo.UserVO;
import com.example.lt.exception.BaseException;
import com.example.lt.mapper.video.VideoMapper;
import com.example.lt.service.FeedService;
import com.example.lt.service.FileService;
import com.example.lt.service.InterestPushService;
import com.example.lt.service.audit.VideoPublishAuditServiceImpl;
import com.example.lt.service.user.FavoritesService;
import com.example.lt.service.user.FollowService;
import com.example.lt.service.user.UserService;
import com.example.lt.service.video.TypeService;
import com.example.lt.service.video.VideoService;
import com.example.lt.service.video.VideoShareService;
import com.example.lt.service.video.VideoStarService;
import com.example.lt.util.DateUtil;
import com.example.lt.util.FileUtil;
import com.example.lt.util.RedisUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author 小涛Tao
 * @Date: 2024/07/29/21:25
 * version 1.0 注释：
 **/
@Service
@RequiredArgsConstructor
public class VideoServiceImpl extends ServiceImpl<VideoMapper, Video> implements VideoService {

    private final VideoMapper videoMapper;

    private final RedisUtil redisUtil;

    private final RedisTemplate redisTemplate;

    private final UserService userService;

    private final FollowService followService;

    private final FeedService feedService;

    private final TypeService typeService;

    private final FileService fileService;

    private final VideoShareService videoShareService;

    private final VideoStarService videoStarService;

    private final FavoritesService favoritesService;

    private final InterestPushService interestPushService;

    private final VideoPublishAuditServiceImpl videoPublishAuditService;

    private ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 发布/修改视频
     * 修改无法更换视频源
     * @param video
     */
    @Override
    public void publishVideo(Video video) {
        final Long userId = BaseContext.get();
        Video oldVideo = null;
        final Long videoId = video.getId();
        if (videoId != null) {
            // url不能一致，我的修改：video和oldVideo的视频url和封面url必须一致
            oldVideo = getOne(new LambdaQueryWrapper<Video>().eq(Video::getId, videoId).eq(Video::getUserId, userId));
            if (!(oldVideo.buildVideoUrl()).equals(video.buildVideoUrl()) || !(oldVideo.buildCoverUrl()).equals(video.buildCoverUrl())) {
                throw new BaseException("不能更换视频源,只能修改视频信息");
            }
        }

        // 判断分类是否存在
        final Type type = typeService.getById(video.getTypeId());
        if (type == null) {
            throw new BaseException("分类不存在");
        }

        boolean isAdd = videoId == null ? true : false; // true：表示是添加视频   false：表示是修改视频
        if (isAdd) {
            // 把分类中的标签加到视频标签中
            final List<String> typeLabels = type.buildLabel();
            Set<String> setLabels = new HashSet<>();
            setLabels.addAll(typeLabels);
            setLabels.addAll(video.buildLabel());
            StringBuilder sb = new StringBuilder();
            for (String label : setLabels) {
                sb.append(label + ",");
            }
            sb.deleteCharAt(sb.lastIndexOf(","));
            video.setLabelNames(sb.toString());
        }

        // 判断标签是否不大于5个
        if (video.buildLabel().size() - type.buildLabel().size() > 5) {
            throw new BaseException("标签最多只能选择5个");
        }

        video.setAuditStatus(AuditStatus.PROCESS); // 审核状态设置为审核中：2
        video.setUserId(userId);

        video.setYv(null);

        if (!isAdd) {
            // 这里把视频的分类、标签、url和封面设置成 null 是因为在更新视频时不需要修改这几个字段，设置为 null 就可以防止这几个字段被修改了
            video.setVideoType(null);
            video.setLabelNames(null);
            video.setUrl(null);
            video.setCover(null);
        } else {

            // 如果没设置封面，就帮他设置一个封面
            if (ObjectUtils.isEmpty(video.getCover())) {
                // 根据视频id生成封面，并设置视频封面
                video.setCover(fileService.generatePhoto(video.getUrl(), userId));
            }
            video.setYv("YV" + UUID.randomUUID().toString().replace("-", "").substring(8));
        }

        // 填充视频时长 (若上次发布视频不存在Duration则会尝试获取)
        if (isAdd || !StringUtils.hasLength(oldVideo.getDuration())) {
            final String uuid = UUID.randomUUID().toString();
            LocalCache.put(uuid, true);
            try {
                Long url = video.getUrl();
                if (url == null || url == 0) url = oldVideo.getUrl();
                final String fileKey = fileService.getById(url).getFileKey();
                final String videoDuration = FileUtil.getVideoDuration(QiNiuConfig.CNAME + "/" + fileKey + "?uuid=" + uuid);
                video.setDuration(videoDuration);
            } finally {
                LocalCache.rem(uuid);
            }
        }

        this.saveOrUpdate(video); // 保存或修改视频信息

        final VideoTask videoTask = VideoTask.builder()
                .oldVideo(video)
                .video(video)
                .isAdd(isAdd)
                .oldState(isAdd ? true : oldVideo.getOpen()) // 设置旧的状态，如果是新添的则设置为1：私密，否则设置为旧视频的状态
                .newState(true) // true（1）：私密   false（0）：公开
                .build();

        if (!isAdd) {
            videoTask.setOldVideo(oldVideo);
            videoTask.setNewState(video.getOpen());
        }

        // 调用内容审核方法
        videoPublishAuditService.audit(videoTask, false);

    }

    /**
     * 获取视频信息
     * @param videoId 视频id
     * @param userId 当前用户
     * @return
     */
    @Override
    public Video getVideoById(Long videoId, Long userId) {
        final Video video = getById(videoId);
        if (ObjectUtils.isEmpty(video)) throw new BaseException("指定视频不存在");

        // 视频是私密的则返回空
        if (video.getOpen()) return new Video();
        setUserVOAndUrl(Collections.singleton(video));
        // TODO 后续看着改
        // 当前视频用户自己是否有收藏/点赞过等信息
        // 这里需要优化 如果这里开线程获取则系统g了(因为这里的场景不适合) -> 请求数很多
        // 正确做法: 视频存储在redis中，点赞收藏等行为异步放入DB, 定时任务扫描DB中不重要更新redis
        video.setStart(videoStarService.starState(videoId, userId));
        video.setFavorites(favoritesService.favoritesState(videoId, userId));
        video.setFollow(followService.isFollows(video.getUserId(), userId));

        return video;
    }

    /**
     * 用户获取推送视频
     * @param userId 用户id
     * @return
     */
    @Override
    public Collection<Video> pushVideos(Long userId) {
        User user = null;
        if (!ObjectUtils.isEmpty(userId)) {
            user = userService.getById(userId);
        }

        Collection<Long> videoIds = interestPushService.listVideoIdByUserModel(user);
        Collection<Video> videos = new ArrayList<>();

        if (ObjectUtils.isEmpty(videoIds)) {
            videoIds = list(new LambdaQueryWrapper<Video>().orderByDesc(Video::getGmtCreated))
                    .stream().map(Video::getId).collect(Collectors.toList());
            videoIds = new HashSet<>(videoIds).stream().limit(10).collect(Collectors.toSet());
        }

        videos = listByIds(videoIds);
        setUserVOAndUrl(videos); // 设置用户信息和视频格式
        return videos;
    }

    /**
     * 用户拉取模式
     * @param userId 用户id
     * @return
     */
    @Override
    public void initFollowFeed(Long userId) {
        // 获取 userId 所有关注人的id，用于给用户更新收件箱
        final Collection<Long> followIds = followService.getFollow(userId, null);
        // 更新用户的收件箱
        feedService.initFollowFeed(userId, followIds);
    }

    /**
     * 关注流
     * @param userId 用户id
     * @param lastTime 滚动分页参数，首次为 now Data()，后续为上次的末尾视频时间
     * @return
     */
    @Override
    public Collection<Video> followFeed(Long userId, Long lastTime) {
        lastTime = lastTime == null ? new Date().getTime() : lastTime;
        long nowTime = new Date().getTime() - 40000;

        // 从redis系统【 in:follow:feed:userId 】中获取【视频id】集合
        Set<Long> set = redisTemplate.opsForZSet()
                .reverseRangeByScore(RedisConstant.IN_FOLLOW + userId, 0, lastTime, lastTime > nowTime ? 0 : 1, 5);
        if (ObjectUtils.isEmpty(set)) {
            // 用户超过七天未登录，并且在此往前的七天内也没有关注人发布视频，所以会查不到视频id
            return Collections.EMPTY_LIST;
        }

        // 获取视频信息，并按照视频创建时间降序排序，也就是越新的视频在前面
        final Collection<Video> videos = list(new LambdaQueryWrapper<Video>().in(Video::getId, set).orderByDesc(Video::getGmtCreated));

        setUserVOAndUrl(videos); // 设置用户信息和视频格式等信息

        return videos;
    }

    /**
     * 获取当前审核队列是快还是慢
     * @return
     */
    @Override
    public String getAuditQueueState() {
        return videoPublishAuditService.getAuditQueueState() ? "快速" : "慢速";
    }


    /**
     * 查询用户所管理的视频
     * @param basePage
     * @return
     */
    @Override
    public IPage<Video> listByUserIdVideo(BasePage basePage, Long userId) {
        // 分页查询 userId 下的视频，并按降序排序
        final IPage page = page(basePage.page(), new LambdaQueryWrapper<Video>().eq(Video::getUserId, userId).orderByDesc(Video::getGmtCreated));
        // 为page中的结果（List<Video>）中的每一个视频设置用户信息和视频格式
        setUserVOAndUrl(page.getRecords()); // 给每一个视频设置用户信息和视频格式
        return page;
    }

    /**
     * 根据用户id分页获取视频，只包含公开的视频
     * @param userId 用户id
     * @param basePage 分页信息
     * @reture
     */
    @Override
    public IPage<Video> listByUserIdOpenVideo(Long userId, BasePage basePage) {
        if (userId == null) return new Page<>();

        final IPage<Video> page = page(basePage.page(), new LambdaQueryWrapper<Video>()
                .eq(Video::getUserId, userId)
                .eq(Video::getAuditStatus, AuditStatus.SUCCESS)
                .orderByDesc(Video::getGmtCreated));

        final List<Video> videos = page.getRecords();
        // 因为 List<video> 是引用类型，所以这里的 videos 是地址引用，指向的是 page.getRecords() 的地址
        setUserVOAndUrl(videos); // 给每一个视频设置用户信息和视频格式
        return page;
    }

    /**
     * 获取用户 userId 所有公开视频的id集合
     * @param userId 用户id
     * @return
     */
    @Override
    public List<Long> listVideoIdByUserId(Long userId) {
        // 查询用户所有公开且审核通过的视频，并提取视频的id为一个List集合返回
        final List<Long> videoIds = list(new LambdaQueryWrapper<Video>()
                .eq(Video::getUserId, userId)
                .eq(Video::getOpen, 0)
                .eq(Video::getAuditStatus, AuditStatus.SUCCESS))
                .stream()
                .map(Video::getId)
                .collect(Collectors.toList());

        return videoIds; // 返回视频id集合
    }

    /**
     * 根据分类获取视频，乱序
     * @param typeId 分类id
     * @return
     */
    @Override
    public Collection<Video> getVideosByTypeId(Long typeId) {
        // 如果 typeId 为空，或者是分类表中没有对应的分类，则直接返回空集合
        if (typeId == null) return Collections.EMPTY_LIST;
        final Type type = typeService.getById(typeId);
        if (ObjectUtils.isEmpty(type)) return Collections.EMPTY_LIST;

        // 根据分类id去系统存储中随机获取视频id
        final Collection<Long> videoIds = interestPushService.listVideoIdByTypeId(typeId);
        if (ObjectUtils.isEmpty(videoIds)) return Collections.EMPTY_LIST; // 判断视频id集合是否为空
        final Collection<Video> videos = listByIds(videoIds); // 查询视频信息

        setUserVOAndUrl(videos); // 给每一个视频设置用户信息和视频格式
        return videos;
    }

    /**
     * 用户 收藏/取消收藏 视频
     * @param favoritesId 收藏夹id
     * @param videoId 视频id
     * @return true：收藏操作   false：取消收藏操作
     */
    @Override
    public boolean favoritesVideo(Long favoritesId, Long videoId) {
        // 验证要收藏的视频是否存在
        final Video video = getById(videoId);
        if (ObjectUtils.isEmpty(video)) throw new BaseException("该视频不存在");

        // 调用方法，并记录这次操作是【收藏】还是【取消收藏】，true：收藏操作   false：取消收藏操作
        final boolean favoritesState = favoritesService.favorites(favoritesId, videoId);
        // 修改【视频表】中的【被收藏总数】
        updateFavoritesCount(video, favoritesState ? 1L : -1L);

        final List<String> labels = video.buildLabel(); // 获取视频的标签
        // 如果是【收藏操作】则是 +2.0 ，如果是【取消收藏操作】则是 -1.0
        final Double score = favoritesState ? 2.0 : -1.0;
        final UserModel userModel = UserModel.buildUserModel(labels, videoId, score);
        interestPushService.updateUserModel(userModel); // 调用更新用户推送模型方法

        return favoritesState;
    }

    /**
     * 获取收藏夹下的视频
     * @param favoriteId 收藏夹id
     * @return
     */
    @Override
    public Collection<Video> listVideoByFavorites(Long favoriteId) {
        // 获取【视频id】集合
        final List<Long> videoIds = favoritesService.listVideoIds(favoriteId, BaseContext.get());
        if (ObjectUtils.isEmpty(videoIds)) {
            return Collections.EMPTY_LIST;
        }
        final Collection<Video> videos = listByIds(videoIds);
        setUserVOAndUrl(videos); // 给每一个视频设置用户信息和视频格式
        return videos;
    }


    /**
     * 用户点赞
     * @param videoId 视频id
     * @return
     */
    @Override
    public boolean starVideo(Long videoId) {
        // 检验视频是否存在
        final Video video = getById(videoId);
        if (ObjectUtils.isEmpty(video)) throw new BaseException("该视频不存在");

        final boolean starState = videoStarService.starVideo(videoId, BaseContext.get());
        updateStarCount(video, starState ? 1L : -1L); // 修改视频表中的总点赞字段

        final List<String> labels = video.buildLabel();
        // TODO 这里取消点赞是减分
        // 如果是【点赞操作】则是 +1.0 ，如果是【取消点赞操作】则是 -0.3
        final Double score = starState ? 1.0 : -0.3;
        final UserModel userModel = UserModel.buildUserModel(labels, videoId, score);
        interestPushService.updateUserModel(userModel); // 修改用户推送模型

        return starState;
    }

    /**
     * 添加视频浏览记录到 redis 中
     * @param videoId 视频id
     * @param userId 用户id
     */
    @Override
    public void historyVideo(Long videoId, Long userId) {
        final String key = RedisConstant.HISTORY_VIDEO + videoId + ":" + userId;
        final Object value = redisUtil.get(key);
        if (value == null) {
            final Video video = getById(videoId);
            if (ObjectUtils.isEmpty(video)) throw new BaseException("该视频不存在");
            redisUtil.set(key, videoId, RedisConstant.HISTORY_TIME);
            video.setUser(userService.getInfo(video.getUserId()));
            video.setTypeName(typeService.getById(video.getTypeId()).getName());
            // TODO 这里设置的时间没有用，需要写一个方法去删除redis中超过7天的浏览记录
            redisUtil.zadd(RedisConstant.USER_HISTORY_VIDEO + userId, new Date().getTime(), video, RedisConstant.HISTORY_TIME);
            updateHistory(video, 1L);
        }
    }

    /**
     * 获取当前用户浏览记录,带分页
     * @param userId 用户id
     * @param basePage 分页信息
     * @return
     */
    @Override
    public LinkedHashMap<String, List<Video>> getHistory(Long userId, BasePage basePage) {
        final String key = RedisConstant.USER_HISTORY_VIDEO + userId;
        final Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisUtil.zSetGetByPage(key, basePage.getPage(), basePage.getLimit());
        if (ObjectUtils.isEmpty(typedTuples)) {
            return new LinkedHashMap<>();
        }
        List<Video> temp = new ArrayList<>();
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        final LinkedHashMap<String, List<Video>> result = new LinkedHashMap<>();
        for (ZSetOperations.TypedTuple typedTuple : typedTuples) {
            final Date date = new Date(typedTuple.getScore().longValue());
            final String format = simpleDateFormat.format(date); // 把格式转换成【年-月-日】
            if (!result.containsKey(format)) {
                result.put(format, new ArrayList<>());
            }
            final Video video = (Video)typedTuple.getValue();
            result.get(format).add(video); // 以【年-月-日】为key来添加【video】
            // 这里将【视频video】加入到【temp】集合中
            temp.add(video);
        }
        setUserVOAndUrl(temp); // 因为【video】是【引用类型】，所以修改【temp】中的video，也会修改【result】值里面的【video】
        return result;
    }

    /**
     * 用户登录时，删除七天前的历史浏览记录
     * @param userId
     */
    public boolean deleteHistory(Long userId) {
        final String key = RedisConstant.USER_HISTORY_VIDEO + userId;

        // 得到 7 天前的这个时间
        final Date curDate = new Date();
        final Date preDate = DateUtil.addDateDays(curDate, -7);

        // 删除七天前的历史浏览记录，也就是分数从 0 到 preDate.getTime() 的元素
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, preDate.getTime());
        return true;
    }

    /**
     * 根据视频标签推送相似视频
     * @param video
     * @return
     */
    @Override
    public Collection<Video> listSimilarVideo(Video video) {
        if (ObjectUtils.isEmpty(video) || ObjectUtils.isEmpty(video.getLabelNames())) return Collections.EMPTY_LIST;
        final List<String> labels = video.buildLabel();
        final ArrayList<String> labelNames = new ArrayList<>();
        labelNames.addAll(labels);
        labelNames.addAll(labels);
        final Set<Long> videoIds =  (Set<Long>) interestPushService.listVideoIdByLabels(labelNames);

        Collection<Video> videos = new ArrayList<>();
        videoIds.remove(video.getId());

        if (!ObjectUtils.isEmpty(videoIds)) {
            videos = listByIds(videoIds);
            setUserVOAndUrl(videos);
        }
        return videos;
    }

    /**
     * 获取热度排行榜
     * @return
     */
    @Override
    public Collection<HotVideo> hotRank() {
        final Set<ZSetOperations.TypedTuple<Object>> zSet = redisTemplate.opsForZSet().reverseRangeWithScores(RedisConstant.HOT_RANK, 0, -1);

        final ArrayList<HotVideo> hotVideos = new ArrayList<>(); // 创建结果集合

        for (ZSetOperations.TypedTuple<Object> objectTypedTuple : zSet) {
            final HotVideo hotVideo;
            try {
                // 进行反序列化
                hotVideo = objectMapper.readValue(objectTypedTuple.getValue().toString(), HotVideo.class);
                hotVideo.setHot((double)(objectTypedTuple.getScore().intValue())); // 设置热度
                hotVideo.hotFormat(); // 设置格式
                hotVideos.add(hotVideo);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return hotVideos;
    }


    /**
     * 查询【i】天内的视频，视频id大于【id】，且查询【limit】条记录
     * @param id 视频id，后续要查询的视频id都要在这个id的后面
     * @param days 天数
     * @param limit 每次查询的数量限制
     * @return
     */
    @Override
    public List<Video> listNDaysAgeVideo(long id, int days, int limit) {
        return videoMapper.listNDaysAgeVideo(id, days, limit);
    }

    /**
     * 获取热门视频
     * @return
     */
    @Override
    public Collection<Video> listHotVideo() {
        Calendar calendar = Calendar.getInstance();
        int today = calendar.get(Calendar.DATE);

        final HashMap<String, Integer> map = new HashMap<>();
        map.put(RedisConstant.HOT_VIDEO + today, 4);
        map.put(RedisConstant.HOT_VIDEO + (today - 1), 3);
        map.put(RedisConstant.HOT_VIDEO + (today - 2), 2);

        // 从redis中获取对应的id，从每一个key中随机获取
        final List hotVideoIds = redisUtil.pipeline(connection -> {
            map.forEach((k, v) -> {
                connection.sRandMember(k.getBytes(), v);
            });
            return null;
        });

        if (ObjectUtils.isEmpty(hotVideoIds)) {
            return Collections.EMPTY_LIST;
        }

        final ArrayList<Long> videoIds = new ArrayList<>();
        for (Object ids : hotVideoIds) {
            if (!ObjectUtils.isEmpty(ids)) {
                videoIds.addAll((List)ids);
            }
        }
        if (ObjectUtils.isEmpty(videoIds)) {
            return Collections.EMPTY_LIST;
        }
        // 这里的【videoIds】不用和历史浏览记录做交集去重

        final Collection<Video> videos = listByIds(videoIds);
        setUserVOAndUrl(videos);
        return videos;
    }


    /**
     * 删除视频
     * @param id 视频id
     */
    @Override
    public void deleteVideo(Long id) {
        if (ObjectUtils.isEmpty(id)) {
            throw new BaseException("指定删除的视频不存在");
        }

        final Long userId = BaseContext.get();
        final Video video = this.getOne(new LambdaQueryWrapper<Video>().eq(Video::getId, id).eq(Video::getUserId, userId));
        if (ObjectUtils.isEmpty(video)) {
            throw new BaseException("指定删除的视频不存在");
        }

        final boolean b = removeById(id);
        if (b) {
            // 另起一个线程去执行删除视频后的影响操作
            new Thread(() -> {
                // 删除视频点赞和分享记录
                videoShareService.remove(new LambdaQueryWrapper<VideoShare>().eq(VideoShare::getVideoId, id).eq(VideoShare::getUserId, userId));
                videoStarService.remove(new LambdaQueryWrapper<VideoStar>().eq(VideoStar::getVideoId, id).eq(VideoStar::getUserId, userId));
                // 删除系统分类和标签库中的视频id
                interestPushService.deleteSystemStockIn(video);
                interestPushService.deleteSystemTypeStockIn(video);
            }).start();
        }
    }

    /**
     * 视频分享
     * @param videoShare
     */
    @Override
    public boolean shareVideo(VideoShare videoShare) {
        final Video video = getById(videoShare.getVideoId());
        if (video == null) throw new BaseException("指定视频不存在");
        final boolean result = videoShareService.share(videoShare);
        if (result) {
            updateShareCount(video, 1L);
        }
        return result;
    }

    /**
     * 搜索视频
     * @param search 搜索关键字
     * @param basePage 分页信息
     * @param userId 用户id
     * @return
     */
    @Override
    public IPage<Video> searchVideo(String search, BasePage basePage, Long userId) {
        final IPage p = basePage.page();
        // 如果带YV则精准搜该视频
        final LambdaQueryWrapper<Video> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Video::getAuditStatus, AuditStatus.SUCCESS);
        if (search.contains("YV")) {
            wrapper.eq(Video::getYv, search);
        } else {
            wrapper.like(search != null, Video::getTitle, search);
        }

        IPage<Video> page = this.page(p, wrapper);
        final List<Video> videos = page.getRecords();
        setUserVOAndUrl(videos);

        userService.addSearchHistory(userId, search);

        return page;
    }

    /**
     * 视频审核放行处理
     * @param video
     */
    @Override
    public void auditProcess(Video video) {
        updateById(video);
        // 放行后的操作
        interestPushService.pushSystemStockIn(video); // 把视频id添加到系统标签库中
        interestPushService.pushSystemTypeStockIn(video); // 把视频id添加到系统分类库中

        // 如果这个视频是在七天内发布的，则审核通过的同时还推送视频id到该视频博主的发件箱
        final Date date = DateUtil.addDateDays(new Date(), -7);
        if (video.getGmtCreated().getTime() > date.getTime()) {
            feedService.pusOutBoxFeed(video.getUserId(), video.getId(), video.getGmtCreated().getTime());
        }
    }


    /**
     * 为视频集合 videos 中的每一个视频设置用户信息 userVO 和视频格式 VideoType（File中的format属性）
     * @param videos 视频集合
     */
    public void setUserVOAndUrl(Collection<Video> videos) {
        // 视频集合不为空才会执行
        if (!ObjectUtils.isEmpty(videos)) {
            final Set<Long> userIds = new HashSet<>();
            final ArrayList<Long> fileIds = new ArrayList<>();
            for (Video video : videos) {
                userIds.add(video.getUserId()); // 获取视频的发布者Id
                fileIds.add(video.getUrl()); // 获取视频源文件的Id
                fileIds.add(video.getCover()); // 获取视频封面文件的Id
            }

            // 批量查询用户基本信息User，并将结果转换为 userId为key User对象为value 的 Map集合
            final Map<Long, User> userMap = userService.list(userIds)
                    .stream().collect(Collectors.toMap(User::getId, Function.identity()));

            // 批量查询文件信息File，并将结果转换为 fileId为key File对象为value 的 Map集合
            final Map<Long, File> fileMap = fileService.listByIds(fileIds)
                    .stream().collect(Collectors.toMap(File::getId, Function.identity()));

            for (Video video : videos) {
                final User user = userMap.get(video.getUserId());
                final UserVO userVO = UserVO.builder()
                        .id(user.getId())
                        .nickName(user.getNickName())
                        .avatar(user.getAvatar())
                        .sex(user.getSex())
                        .description(user.getDescription())
                        .build();
                video.setUser(userVO); // 设置用户信息 userVO
                final File file = fileMap.get(video.getUrl());
                video.setVideoType(file.getFormat()); //设置视频格式 VideoType（File中的format属性）
            }
        }
    }


    /**
     * 修改【视频表】中的【被收藏总数】字段
     * @param video 视频信息
     * @param value 视频被收藏总数的【加减值】
     */
    public void updateFavoritesCount(Video video, Long value) {
        final UpdateWrapper<Video> updateWrapper = new UpdateWrapper<>();
        updateWrapper.setSql("favorites_count = favorites_count + " + value); // 设置sql语句

        updateWrapper.lambda().eq(Video::getId, video.getId()).eq(Video::getFavoritesCount, video.getFavoritesCount());

        update(video, updateWrapper); // 修改视频被收藏总数

//        update(video, new UpdateWrapper<Video>()
//                .setSql("favorites_count = favorites_count + " + value)
//                .lambda()
//                .eq(Video::getId, video.getId())
//                .eq(Video::getFavoritesCount, video.getFavoritesCount()));

    }

    /**
     * 修改【视频表】中的【被点赞总数】字段
     * @param video 视频信息
     * @param value 被点数的【加减值】
     */
    public void updateStarCount(Video video, Long value) {
        final UpdateWrapper<Video> updateWrapper = new UpdateWrapper<>();
        updateWrapper.setSql("start_count = start_count + " + value);
        updateWrapper.lambda().eq(Video::getId, video.getId()).eq(Video::getStartCount, video.getStartCount());
        update(video, updateWrapper);
    }

    /**
     * 修改【视频表】中的【被浏览总数】字段，修改浏览量
     * @param video 视频信息
     * @param value 加减值
     */
    private void updateHistory(Video video, Long value) {
        final UpdateWrapper<Video> updateWrapper = new UpdateWrapper<>();
        updateWrapper.setSql("history_count = history_count + " + value);
        updateWrapper.lambda().eq(Video::getId, video.getId()).eq(Video::getHistoryCount, video.getHistoryCount());
        update(video, updateWrapper);
    }

    /**
     * 修改【视频表】中的【被分享总数】字段，修改分享次数
     * @param video 视频信息
     * @param value 加减值
     */
    private void updateShareCount(Video video, long value) {
        final UpdateWrapper<Video> updateWrapper = new UpdateWrapper<>();
        updateWrapper.setSql("share_count = share_count + " + value);
        updateWrapper.lambda().eq(Video::getId, video.getId()).eq(Video::getShareCount, video.getShareCount());
        update(video, updateWrapper);
    }

}
